/* kernel.c - the C part of the kernel */
/* Copyright (C) 1999  Free Software Foundation, Inc.

 This program is free software; you can redistribute it and/or modify
 it under the terms of the GNU General Public License as published by
 the Free Software Foundation; either version 2 of the License, or
 (at your option) any later version.

 This program is distributed in the hope that it will be useful,
 but WITHOUT ANY WARRANTY; without even the implied warranty of
 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 GNU General Public License for more details.

 You should have received a copy of the GNU General Public License
 along with this program; if not, write to the Free Software
 Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. */

#include <multiboot.h>

/* Macros. */

/* Check if the bit BIT in FLAGS is set. */
#define CHECK_FLAG(flags,bit)   ((flags) & (1 << (bit)))

/* Some screen stuff. */
/* The number of columns. */
#define COLUMNS                 80
/* The number of lines. */
#define LINES                   24
/* The attribute of an character. */
#define ATTRIBUTE               7
/* The video memory address. */
#define VIDEO                   0xB8000

#define PORT 0x3f8   /* COM1 */

#define RTCctrl 0x70
#define RTCdata 0x71

#define RTCsec  0x00
#define RTCmin  0x02
#define RTChr   0x04

/* Variables. */
/* Save the X position. */
static int xpos;
/* Save the Y position. */
static int ypos;
/* Point to the video memory. */
static volatile unsigned char *video;

/* Forward declarations. */
void cmain(unsigned long magic, unsigned long addr);
static void cls(void);
static void itoa(char *buf, int base, int d);
static void putchar(int c);
void printf(const char *format, ...);

void malloc(int size);
void mfree(int position);
int nextFreePosition(int requestAmountRequested);
int allocationAmount(int size);
int mavailableMemory();
int msize();
int unitAllocationBitMap;
int sizeBitMap;
int sizeAllMemory;
int bitmap[13];
unsigned char *memory[104];
unsigned char keyboardus[128];
void keyboardget();

static __inline__ void disable()
{
   __asm__("cli");
}

//static inline
unsigned char inb(unsigned short port)
{
    unsigned char ret;
    __asm__ volatile ("inb %1, %0" : "=a"(ret) : "Nd"(port));
    return ret;
}

static inline void outb(unsigned short port, unsigned char val)
{
    __asm__ volatile ("outb %0, %1" : : "a"(val), "Nd"(port));
}

static __inline__ unsigned long long rdtsc(void)
{
   unsigned long long int x;
   __asm__ volatile (".byte 0x0f, 0x31"
		: "=A" (x));
   return x;
}

void init_serial() {
   outb(PORT + 1, 0x00);    // Disable all interrupts
   outb(PORT + 3, 0x80);    // Enable DLAB (set baud rate divisor)
   outb(PORT + 0, 0x03);    // Set divisor to 3 (lo byte) 38400 baud
   outb(PORT + 1, 0x00);    //                  (hi byte)
   outb(PORT + 3, 0x03);    // 8 bits, no parity, one stop bit
   outb(PORT + 2, 0xC7);    // Enable FIFO, clear them, with 14-byte threshold
   outb(PORT + 4, 0x0B);    // IRQs enabled, RTS/DSR set
}

int is_transmit_empty() {
   return inb(PORT + 5) & 0x20;
}

void write_serial(char a) {
   while (is_transmit_empty() == 0);

   outb(PORT, a);
}

/* Check if MAGIC is valid and print the Multiboot information structure
 pointed by ADDR. */
void cmain(unsigned long magic, unsigned long addr) {
	char * boot_loader_name = (char *)addr + 64;
	multiboot_info_t *mbi;
	mbi = (multiboot_info_t *) addr;
	int totalSize = (mbi->mem_upper - mbi->mem_lower) * 1024;
	int unitAllocationBitMap = 8;
	cls();
	sizeBitMap = ((int)(totalSize / (unitAllocationBitMap + 1)));
	sizeAllMemory = (totalSize - sizeBitMap);
	memory[sizeAllMemory];
	bitmap[sizeBitMap];
	/*
	printf("Memoria com tamanho de.....: %dBytes\n",totalSize);
	printf("Memoria Livre..............: %dBytes\n", sizeAllMemory);
	printf("Tamanho Bit Map............: %d\n", unitAllocationBitMap);
	printf("Tamanho Alocacao Bit Map...: %d\n\n\n", sizeBitMap);

	printf("Tamanho Memoria............: %dBytes\n", msize());
	//printf("Memoria Livre..............: %dBytes\n", mavailableMemory());
	printf("Alocar 2 no Bit Map......... \n");
	//malloc(2);
	*/

	/*
	init_serial();

	char* msg = "using the serial....\0";
	int i = 0;
	while (msg[i] != '\0') {
		write_serial(msg[i]);
		i++;
	}
	*/

	/* Very simple clock speed calculator */
	/*disable(); // disable interrupts (if still not done)

	unsigned long long stsc = 0; // ciclo inicial do instante
	unsigned long long etsc = 0; // ciclo final do instante
	unsigned long long ttsc = 0; // total de ciclos
	unsigned long long td = 0; // tempo decorrido
	unsigned long long initSec = 0; // segundo inicial
	unsigned long long endSec = 0; // segundo final
	int count = 0;

	while (count < 2) {

		outb(RTCctrl, RTCsec);
		td = inb(RTCdata);
		if(initSec == 0 && td != initSec) {
			outb(RTCctrl, RTCsec);
			stsc = rdtsc();
			initSec = inb(RTCdata);
			count++;
		} else if (td != initSec) {
			outb(RTCctrl, RTCsec);
			etsc = rdtsc();
			endSec = inb(RTCdata);
			count++;
		}
	}

	td = endSec - initSec;
	ttsc = (etsc - stsc);// 1000000;
	ttsc = ttsc / 1000000;

	printf("Tempo Decorrido %ds\n", td);
	printf("Clock CPU %d MHz", ttsc);
	*/

	// teclado a partir daqui!
//	disable();
	cls();
	printf("Testar Teclado Abaixo!\n");
	keyboardget();


} // end cmain

/* Clear the screen and initialize VIDEO, XPOS and YPOS. */
static void cls(void) {
	int i;

	video = (unsigned char *) VIDEO;

	for (i = 0; i < COLUMNS * LINES * 2; i++)
		*(video + i) = 0;

	xpos = 0;
	ypos = 0;
}

/* Convert the integer D to a string and save the string in BUF. If
 BASE is equal to 'd', interpret that D is decimal, and if BASE is
 equal to 'x', interpret that D is hexadecimal. */
static void itoa(char *buf, int base, int d) {
	char *p = buf;
	char *p1, *p2;
	unsigned long ud = d;
	int divisor = 10;

	/* If %d is specified and D is minus, put `-' in the head. */
	if (base == 'd' && d < 0) {
		*p++ = '-';
		buf++;
		ud = -d;
	} else if (base == 'x')
		divisor = 16;

	/* Divide UD by DIVISOR until UD == 0. */
	do {
		int remainder = ud % divisor;

		*p++ = (remainder < 10) ? remainder + '0' : remainder + 'a' - 10;
	} while (ud /= divisor);

	/* Terminate BUF. */
	*p = 0;

	/* Reverse BUF. */
	p1 = buf;
	p2 = p - 1;
	while (p1 < p2) {
		char tmp = *p1;
		*p1 = *p2;
		*p2 = tmp;
		p1++;
		p2--;
	}
}

/* Put the character C on the screen. */
static void putchar(int c) {
	if (c == '\n' || c == '\r') {
		newline: xpos = 0;
		ypos++;
		if (ypos >= LINES)
			ypos = 0;
		return;
	}

	*(video + (xpos + ypos * COLUMNS) * 2) = c & 0xFF;
	*(video + (xpos + ypos * COLUMNS) * 2 + 1) = ATTRIBUTE;

	xpos++;
	if (xpos >= COLUMNS)
		goto newline;
}

/* Format a string and print it on the screen, just like the libc
 function printf. */
void printf(const char *format, ...) {
	char **arg = (char **) &format;
	int c;
	char buf[20];

	arg++;

	while ((c = *format++) != 0) {
		if (c != '%')
			putchar(c);
		else {
			char *p;

			c = *format++;
			switch (c) {
			case 'd':
			case 'u':
			case 'x':
				itoa(buf, c, *((int *) arg++));
				p = buf;
				goto string;
				break;

			case 's':
				p = *arg++;
				if (!p)
					p = "(null)";

				string: while (*p)
					putchar(*p++);
				break;

			default:
				putchar(*((int *) arg++));
				break;
			}
		}
	}
}

int allocationAmount(int size) {
	int amountValue = 0;
	int	amount = size / unitAllocationBitMap;
	amountValue = amount;
	amount -= amount;

	if(amount > 0) {
		amountValue++;
	}

	return amountValue;
}

int mavailableMemory() {
	int amountFreePositions = 0;
	int i = 0;
	for(; i < sizeBitMap; i++) {
		if(bitmap[i] == 0){
			amountFreePositions++;
		}
	}
	return (amountFreePositions * unitAllocationBitMap);
}

int nextFreePosition(int requestAmountRequested) {
	int amountFreePositions = 0;
	int i = 0;

	for(; i < sizeBitMap; i++) {
		if(amountFreePositions < requestAmountRequested) {
			if(bitmap[i] == 0) {
				amountFreePositions++;
			} else {
				amountFreePositions = 0;
			}
		} else {
			return i-amountFreePositions;
		}
	}

	if(amountFreePositions == requestAmountRequested) {
		return i-amountFreePositions;
	} else {
		return -1;
	}
}

int msize() {
	return sizeAllMemory;
}

void mfree(int position) {
	int amountValue = 0;
	int value = bitmap[position];

	if(value != 0) {
		int i = position;

		while((i <= sizeBitMap-1) && (bitmap[i] >= 1) && (amountValue < value)) {
			bitmap[i] = 0;
			amountValue++;
			i++;
		}

		int j = position;

		if(amountValue < value) {
			while((j > 0) && (bitmap[j-1] >= bitmap[j]) && (amountValue <= value)) {
				bitmap[j-1] = 0;
				amountValue++;
				j--;
			}
		}
	}
}

void malloc(int size) {
	int numberPositionToBeAllocated = allocationAmount(size);
	int firstFreePosition = nextFreePosition(numberPositionToBeAllocated);
	int amount = numberPositionToBeAllocated;



	if(numberPositionToBeAllocated != -1) {
		int mem = firstFreePosition;

		while(amount != -1) {
			bitmap[mem] = amount--;
			mem++;
		}
	} else {
		printf("Memory Full!");
	}

}

void keyboardget()
{
	video = (unsigned char *) VIDEO;
	int j = 160;
	int i = 0;

	while(1 == 1)
	{
		int s = inb(0x60);
		if(s < 128 && i != s)
		{
			i = inb(0x60);

//			video[j] = (unsigned char)keyboardus[j];
			printf(keyboardus[j]);
			j++;
//			video[j] = 0x04;
//			j++;
		}
	}
}

unsigned char keyboardus[128] =
{
    0,  27, '1', '2', '3', '4', '5', '6', '7', '8',	/* 9 */
  '9', '0', '-', '=', '\b',	/* Backspace */
  '\t',			/* Tab */
  'q', 'w', 'e', 'r',	/* 19 */
  't', 'y', 'u', 'i', 'o', 'p', '[', ']', '\n',		/* Enter key */
    0,			/* 29   - Control */
  'a', 's', 'd', 'f', 'g', 'h', 'j', 'k', 'l', ';',	/* 39 */
 '\'', '`',   0,		/* Left shift */
 '\\', 'z', 'x', 'c', 'v', 'b', 'n',			/* 49 */
  'm', ',', '.', '/',   0,					/* Right shift */
  '*',
    0,	/* Alt */
  ' ',	/* Space bar */
    0,	/* Caps lock */
    0,	/* 59 - F1 key ... > */
    0,   0,   0,   0,   0,   0,   0,   0,
    0,	/* < ... F10 */
    0,	/* 69 - Num lock*/
    0,	/* Scroll Lock */
    0,	/* Home key */
    0,	/* Up Arrow */
    0,	/* Page Up */
  '-',
    0,	/* Left Arrow */
    0,
    0,	/* Right Arrow */
  '+',
    0,	/* 79 - End key*/
    0,	/* Down Arrow */
    0,	/* Page Down */
    0,	/* Insert Key */
    0,	/* Delete Key */
    0,   0,   0,
    0,	/* F11 Key */
    0,	/* F12 Key */
    0,	/* All other keys are undefined */
};
